package jmt.gui.common.routingStrategies;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

import jmt.gui.common.routingStrategies.LoadDependentRouting.EmpiricalEntry;

public class EnergyAwareRouting extends RoutingStrategy {
	
    Map<Integer, EmpiricalEntry[]> values = null;//Contains a Array of EmpiricalEntry where the Integer represents the From
	
	public EnergyAwareRouting() {
		description = "Jobs are routed based on the specified class mix.";
	}

	@Override
	public String getName() {
		return "Energy Aware";
	}

	@Override
	@Deprecated
	public Map<Object, Double> getValues() {
		return null;
	}
	
	public Map<Integer, EmpiricalEntry[]> getEmpiricalEntries(){
        return values;
    }

	@Override
	public EnergyAwareRouting clone() {
        EnergyAwareRouting newEa = new EnergyAwareRouting();
        if(values != null && values.keySet() != null){
            for(Integer from : values.keySet()){
                for (EmpiricalEntry entry : this.getEmpiricalEntryForFrom(from)){
                    newEa.addEmpiricalEntryForFrom(from, entry.getNameParameter(), entry.getValueParameter());
                }
            }
        }else{
            newEa.values = null;
        }
        return newEa;
	}

	@Override
	public String getClassPath() {
		return "jmt.engine.NetStrategies.RoutingStrategies.EnergyAwareRoutingStrategy";
	}

    public EmpiricalEntry[] getEmpiricalEntryForFrom(Integer i){
        EmpiricalEntry[] out = null;
        if(values != null) {
            out = values.get(i);
        }
        return out;
    }
    
    public void addEmpiricalEntryForFrom(Integer from, String stationName, Double prob){
        EmpiricalEntry newEntry = new EmpiricalEntry();
        newEntry.setNameParameter(stationName);
        newEntry.setValueParameter(prob);
        if(values != null){
            EmpiricalEntry[] existingEntries = values.get(from);
            if(existingEntries!= null && existingEntries.length>0){
                List<EmpiricalEntry> a = Arrays.asList(existingEntries);
                List<EmpiricalEntry> newlist = new ArrayList<EmpiricalEntry>(a);
                newlist.add(newEntry);
                EmpiricalEntry[] newEntries = new EmpiricalEntry[newlist.size()];
                newEntries = newlist.toArray(newEntries);
                values.put(from,newEntries);
            }else{
                values.put(from, new EmpiricalEntry[]{newEntry});
            }
        }else{
            values = new HashMap<Integer, EmpiricalEntry[]>();
            values.put(from, new EmpiricalEntry[]{newEntry});
        }
    }

    /**
     * Validates the Routing to see if all the probailities sum to 1.
     * If there is no error then it returns List with size 0.
     * @return
     */
    public List<String> validate(){
        List<String> errors = new ArrayList<String>(0);
        List<String> warnings = new ArrayList<String>(0);

        if(this.getEmpiricalEntries() == null || this.getEmpiricalEntries().size() == 0){
            errors.add("No routing defined");
        }
        if(errors.size() == 0) {
        	List<Integer> fromAsList = new ArrayList<Integer>();
        	fromAsList.addAll(this.getEmpiricalEntries().keySet());
        	Collections.sort(fromAsList);
        	for(Iterator<Integer> it = fromAsList.iterator(); it.hasNext(); ) {
        		Integer from = it.next();
        		if(this.getEmpiricalEntries().get(from)[0].getValueParameter() > 1.0) {
        			errors.add("Population mix > 1.0");
        		}
        		if(this.getEmpiricalEntries().get(from)[1].getValueParameter() % 1.0 != 0) {
        			errors.add("Aging is not an integer");
        		}
        		if(this.getEmpiricalEntries().get(from)[2].getValueParameter() % 1.0 != 0) {
        			errors.add("Window size is not an integer");
        		}
        	}
        }
        return errors;
    }

    /**
     * Removes the parameter station name from the entry list.
     * @param stationName
     */
    public void refreshRoutingOnStationDeletion(String stationName){
    	EnergyAwareRouting cloneRouting = (EnergyAwareRouting)this.clone();
        this.values.clear();
        for(Iterator<Integer> itEntries = cloneRouting.values.keySet().iterator(); itEntries.hasNext();){
            Integer from = itEntries.next();
            EmpiricalEntry[] entries = cloneRouting.getEmpiricalEntryForFrom(from);
            for(EmpiricalEntry entry : entries){
                if(!entry.getNameParameter().equalsIgnoreCase(stationName)){
                    this.addEmpiricalEntryForFrom(from, entry.getNameParameter(), entry.getValueParameter());
                }
            }
        }
    }

    public void refreshRouting(Set<String> fstationNames){
        if(this.values != null && this.values.size() > 0){
            Integer[] froms = new Integer[this.values.keySet().size()];
            this.values.keySet().toArray(froms);
            for(Integer from : froms){
                List<EmpiricalEntry> newEntries = new ArrayList<EmpiricalEntry>(0);
                Set<String> oldFStationNames = new HashSet<String>(0);
                EmpiricalEntry[] entries = this.getEmpiricalEntryForFrom(from);
                for(EmpiricalEntry entry : entries){
                    if(fstationNames.contains(entry.nameParameter)){
                        newEntries.add(entry);
                    }
                    oldFStationNames.add(entry.getNameParameter());
                }
                for(String fstationName : fstationNames){
                    if(!oldFStationNames.contains(fstationName)){
                        EmpiricalEntry newEntry = new EmpiricalEntry();
                        newEntry.setNameParameter(fstationName);
                        newEntry.setValueParameter(0);
                        newEntries.add(newEntry);
                    }
                }
            }
        }
    }

    /**
     * Returns true if the routing strategy is dependent from the state of
     * the model
     *
     * @return true if the routing strategy is dependent from the state of
     *         the model
     *         <p/>
     *         Author: Francesco D'Aquino
     */
    @Override
    public boolean isModelStateDependent() {
        return false;  //To change body of implemented methods use File | Settings | File Templates.
    }

    public class EmpiricalEntry{

    	private String nameParameter;
    	private double valueParameter;
    	
    	public void EmpiricalEntry() {
    		
    	}

        public void setValueParameter(double val) {
            this.valueParameter = val;
        }

        public double getValueParameter() {
            return valueParameter;
        }


        public String getNameParameter() {
            return nameParameter;
        }

        public void setNameParameter(String name) {
            this.nameParameter = name;
        }
    }

}